﻿/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

(function () {
	var plugin = {
		name: 'highlight',
		className: 'plugin' + this.name,
		console: window.console || { log: function () { window.console && window.console.log && window.console.log(arguments[0]); } },
		instances: {
			add: function (editor) {
				this[editor.name] = new highlight(editor);
			}
		},
		keys: { BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, PAUSE: 19, CAPS_LOCK: 20, ESCAPE: 27, SPACE: 32, PAGE_UP: 33, PAGE_DOWN: 34, END: 35, HOME: 36, LEFT_ARROW: 37, UP_ARROW: 38, RIGHT_ARROW: 39, DOWN_ARROW: 40, INSERT: 45, DELETE: 46, HELP: 47, LEFT_WINDOW: 91, RIGHT_WINDOW: 92, SELECT: 93, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, NUMPAD_MULTIPLY: 106, NUMPAD_PLUS: 107, NUMPAD_ENTER: 108, NUMPAD_MINUS: 109, NUMPAD_PERIOD: 110, NUMPAD_DIVIDE: 111, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125, F15: 126, NUM_LOCK: 144, SCROLL_LOCK: 145 },
		getHighLight: function (editor) {
			return this.instances[editor.name];
		},
		addInstance: function (editor) {
			editor.on('contentDom', function () {
				plugin.instances.add(editor);
			});
			editor.on('destroy', function (evt) {
				var editor = evt.editor,
					hl_instance = plugin.getHighLight(editor);
				if (!hl_instance)
					return;
				delete plugin.instances[editor.name];
				hl_instance.destroy(true);
			});
			editor.on('afterSetData', function () {
				if (plugin.isScaytEnabled(editor)) {
					window.setTimeout(function () {
						var hl_instance = plugin.getHighLight(editor);
						hl_instance && hl_instance.refresh();
					}, 10);
				}
			});
			editor.on('beforeCommandExec', function (evt) {
				// Disable SCAYT before Source command execution.
				if ((evt.data.name == 'source' || evt.data.name == 'newpage') && editor.mode == 'wysiwyg') {
					var hl_instance = plugin.getHighLight(editor);
					if (hl_instance) {
						hl_instance.destroy(true);
						delete plugin.instances[editor.name];
					}
				}
				// Catch on source mode switch off (#5720)
				else if (evt.data.name == 'source' && editor.mode == 'source') {

				}
			});
			editor.on('afterCommandExec', function (evt) {
				if (editor.mode == 'wysiwyg' && (evt.data.name == 'undo' || evt.data.name == 'redo'))
					window.setTimeout(function () { plugin.getHighLight(editor).refresh(); }, 10);
			});
			editor.on('insertElement', function () {
				var hl_instance = plugin.getHighLight(editor);
				if (CKEDITOR.env.ie)
					editor.getSelection().unlock(true);

				// Return focus to the editor and refresh SCAYT markup (#5573).
				window.setTimeout(function () {
					hl_instance.focus();
					hl_instance.refresh();
				}, 10);
			}, this, null, 50);

			editor.on('insertHtml', function () {
				var hl_instance = plugin.getHighLight(editor);
				if (CKEDITOR.env.ie)
					editor.getSelection().unlock(true);

				// Return focus to the editor (#5573)
				// Refresh SCAYT markup
				window.setTimeout(function () {
					hl_instance.focus();
					hl_instance.refresh();
				}, 10);
			}, this, null, 50);

			var dataProcessor = editor.dataProcessor,
			htmlFilter = dataProcessor && dataProcessor.htmlFilter;

			if (htmlFilter) {
				htmlFilter.addRules({
					elements: {
						span: function (element) {
							if (element.attributes['mc']) {
								delete element.name; // Write children, but don't write this node.
								return element;
							}
						},
						img: function (element) {
							if (element.attributes['mc']) {
								delete element.name;
								return element;
							}
						}
					}
				});
			}


			// Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)
			var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
			undoImagePrototype.equals = CKEDITOR.tools.override(undoImagePrototype.equals, function (org) {
				return function (otherImage) {
					var thisContents = this.contents,
					otherContents = otherImage.contents;
					var hl_instance = plugin.getHighLight(this.editor);
					// Making the comparison based on content without SCAYT word markers.
					if (hl_instance) {
						// scayt::reset might return value undefined. (#5742)
						this.contents = hl_instance.reset(thisContents) || '';
						otherImage.contents = hl_instance.reset(otherContents) || '';
					}

					var retval = org.apply(this, arguments);

					this.contents = thisContents;
					otherImage.contents = otherContents;
					return retval;
				};
			});

			if (editor.document)
				this.instances.add(editor);
		}
	};
	var highlight = CKEDITOR.plugins.highlight = function (editor) {
		this.editor = editor;
		this.init();
	};

	CKEDITOR.tools.extend(highlight, plugin);
	CKEDITOR.tools.extend(highlight.prototype, {
		span: new CKEDITOR.dom.element('span'),
		config: [],
		document: null,
		containerNode: null,
		init: function () {
			/// <summary>高亮初始化</summary>
			var t = this;

			t.document = t.editor.document;
			t.containerNode = t.document.getBody();

			//测试代码
			var body = t.containerNode;
			body.append(new CKEDITOR.dom.text('aa阿狄森病aa公'));
			body.append(new CKEDITOR.dom.text(''));
			body.append(new CKEDITOR.dom.text('私ccc阿狄森氏病aα受体阻滞剂'));

			t.span.data(plugin.name, "on");

			//存储最后一次数据后的html内容，用来判断内容是否发生变化
			t._innerHTML = t.containerNode.getHtml();
			try {
				t._innerHTML = window.external.Highlight(t._innerHTML);
			} catch (e) {

			}
			if (t._innerHTML != t.containerNode.getHtml()) {
				t.containerNode.setHtml(t._innerHTML);
			}
			var evt;
			if (CKEDITOR.env.webKit) {
				evt = { "onkeydown": "onBeforeEvt", "onkeyup": "onAfterEvt", "onmousedown": "onMouseDown" };
			} else {
				evt = { "onkeypress": "onBeforeEvt", "onkeyup": "onAfterEvt", "onmousedown": "onMouseDown" };
			}
			if (CKEDITOR.env.gecko || CKEDITOR.env.webKit || CKEDITOR.env.ie) {
				evt.onpaste = "onAfterEvt";
				evt.oncut = "onAfterEvt";
			}
			if (CKEDITOR.env.ie) {
				evt["onkeydown"] = "onBeforeEvtIe";
			}
			for (x in evt) {
				t.editor.document.on(x.substr(2), t[evt[x]], t);
			}
		},
		setContent: function (func) {
			var t = this;
			var range = t.getRange();
			var bookmark = range && range.createBookmark(true);
			func.apply(t, [bookmark]);
			if (range && bookmark) {
				t.containerNode.$.setActive();
				range.moveToBookmark(bookmark);
			}
			try {
				t._innerHTML = t.containerNode.getHtml();
			}
			catch (e) {
			}
		},
		getParentEx: function () {
			/// <summary>查找当前选择的节点的所有高亮的父节点</summary>
			var hls = { isHighLight: false, hls: [] };
			var t = this;
			var node = t.getSelectionNode();
			while (!t.containerNode.equals(node)) {
				if (t.isHLNode(node)) {
					hls.hls.push(node);
					hls.isHighLight = true;
				}
				node = node.getParent();
			}
		},
		isHLNode: function (node) {
			return (node && node.type == CKEDITOR.NODE_ELEMENT && node.data(plugin.name + '-key'));
		},
		getSelectionNode: function () {
			var t = this;
			var range = t.getRange();
			return range.startContainer;
		},
		getRange: function () {
			var t = this;
			var sel = t.editor.getSelection();
			var ranges = sel && sel.getRanges();
			return ranges && ranges.length && ranges[0];
		},
		getKeyMoving: function () {
			/// <summary>获取所有移动光标的按键</summary>
			var dk = plugin.keys;
			return [dk.HOME, dk.UP_ARROW, dk.DOWN_ARROW, dk.LEFT_ARROW, dk.RIGHT_ARROW, dk.END, dk.PAGE_UP, dk.PAGE_DOWN, 93];
		},
		remove: function (node) {
			if (!arguments.length) {
				return;
			}
			if (!CKEDITOR.tools.isArray(node)) {
				node = [node];
			}
			if (node.length < 500) {
				for (var i = 0; i < node.length; i++) {
					node[i].remove(true);
				}
			}
		},
		getParent: function (node, nodeName, attr, root) {
			var t = this;
			root = root || t.containerNode;
			nodeName = nodeName ? new RegExp("^(?:" + nodeName + ")$") : null;
			while (node && node != root) {
				if (nodeName && nodeName.test(node.$.nodeName.toUpperCase())) {
					return node;
				}
				if (attr) {
					for (var k in attr) {
						if (node.getAttribute(k) !== null && attr[k] === null) {
							return node;
						}
						if ((attr[k] !== null && node.getAttribute(k) !== null && !(node.getAttribute(k) === false)) ? !attr[k] : attr[k]) {
							return node;
						}
					}
				}
				node = node.getParent();
			}
			return false;
		},
		getHLNode: function (node) {
			var t = this;
			if (!arguments.length) {
				try {
					node = t.getSelectionNode();
				}
				catch (e) {
					return null;
				}
			}
			var attName = "data-" + plugin.name + "-key";
			node = t.getParent(node, null, { attName: null });
			return node;
		},
		onBeforeEvtIe: function (evt) {
			var t = this;
			if (evt.keyCode == 13) {
				plugin.console.log("onBeforeEvtIe");
				var hls = t.getParentEx();
				if (hls.isHighLight) {
					t.setContent(function () {
						t.remove(hls.hls);
					});
				}
			}
		},
		onBeforeEvt: function (evt) {
			//这里首先需要判断除开正常的输入按键，粘帖操作意外的按键操作才能继续；判断前后节点是否是高亮节点，如果是的话需要移除前后的高亮节点
			var t = this, keyCode = evt.data.getKey(), keyStore = evt.data.getKeystroke, movings = t.getKeyMoving(), evt$ = evt.data.$;
			if ((evt$.ctrlKey && keyCode != 86) || evt$.altKey || (!CKEDITOR.env.gecko && evt$.shiftKey && !evt$.charCode)) {
				return;
			}
			if ((evt$.altKey && keyCode == 18) || CKEDITOR.tools.indexOf(movings, keyCode) != -1 || keyCode == 9) {
				return;
			}

		},
		onAfterEvt: function (evt) {
			plugin.console.log("onAfterEvt");
			var t = this, keyCode = evt.data.getKey(), keyStore = evt.data.getKeystroke, movings = t.getKeyMoving(), evt$ = evt.data.$;
			if (evt.name == "cut" || evt.name == "paste" || keyStore == CKEDITOR.CTRL + 86 || keyStore == CKEDITOR.SHIFT + 45) {
				//剪切和粘贴
				var par = t.getParentEx();
			}
			//debugger;
			if (t._innerHTML == t.containerNode.getHtml()) {
				return;
			}
			var range = new CKEDITOR.dom.range(t.document), oRange = t.getRange();
			var selNode = t.getSelectionNode();
			plugin.console.log(selNode);
			if (selNode.type != CKEDITOR.NODE_TEXT) {
				return;
			}
			var startNode, endNode = startNode = selNode;

			do {
				var prev = startNode.hasPrevious() && startNode.getPrevious();

				if (prev && prev.type == CKEDITOR.NODE_TEXT || t.isHLNode(prev)) {
					startNode = prev;
				} else {
					prev = null;
				}
			} while (prev != null);
			do {
				var next = endNode.hasNext() && endNode.getNext();
				if (next && next.type == CKEDITOR.NODE_TEXT || t.isHLNode(next)) {
					endNode = next;
				} else {
					next = null;
				}
			} while (next != null);
			var p = selNode.getParent();
			range.setStart(startNode, 0);
			var orgLength = endNode.getText().length;
			range.setEnd(endNode, orgLength);
			this.setContent(function (bookmark) {
				//这里创建了标签后把TEXT节点分散了。所以这里获取内容会出错了。所以这里需要判断是否分割过Text节点
				//debugger;
				if (bookmark && endNode.equals(selNode) && orgLength > selNode.getText().length) {
					endNode = t.document.getById(bookmark.startNode).getNext();
					range.setEnd(endNode, endNode.getText().length);
				}
				var fragment = range.cloneContents();
				var children = fragment.getChildren();
				var text = "", markIndex = -1, markHTML = "";
				for (var i = 0; i < children.count(); i++) {
					var child = children.getItem(i);
					if (child.type == CKEDITOR.NODE_TEXT || (child.type == CKEDITOR.NODE_ELEMENT && !child.data("cke-bookmark"))) {
						text += child.getText();
					}
					if (child.type == CKEDITOR.NODE_ELEMENT && child.data("cke-bookmark")) {
						markIndex = text.length;
						markHTML = child.getOuterHtml();
					}
					//child.remove(true);
				}
				var matchs = [];
				for (var i = 0; i < t.config.length; i++) {
					var key = t.config[i];
					while ((match = key.regexp.exec(text)) != null) {
						matchs.push({ start: match.index, end: match.lastIndex, key: key });
					}
				}
				if (matchs.length == 0) {
					return;
				}
				matchs.sort(function (a, b) {
					/// <summary>倒序排列数组</summary>
					if (a.start < b.start) {
						return -1;
					}
					if (a.start == b.start) {
						return 0;
					}
					if (a.start > b.start) {
						return 1;
					}
				});
				fragment = new CKEDITOR.dom.element('div', t.document);
				var lastIndex = 0;

				for (var i = 0; i < matchs.length; i++) {
					var match = matchs[i];
					if (match.start > lastIndex) {
						var textNode = new CKEDITOR.dom.text(text.substring(lastIndex, match.start))

						var markNode = t.span.clone();
						markNode.append(textNode);
						fragment.append(markNode);
						if (markIndex > lastIndex && markIndex < match.start) {
							t.mark(textNode, markIndex - lastIndex, bookmark);
						}
						markNode.remove(true);
					}
					var sNode = t.span.clone();
					sNode.setText(text.substring(match.start, match.end));
					sNode.setStyles({ 'color': match.key.ForeColor, 'background-color': match.key.BackColor });
					sNode.data(plugin.name + '-key', match.key.Key);
					if (match.key.Suggest) {
						sNode.data(plugin.name + '-suggest', match.key.Suggest);
					}
					fragment.append(sNode);

					if (markIndex > match.start && markIndex < match.end) {
						t.mark(sNode, markIndex - match.start, bookmark);
					}
					lastIndex = match.end;
				}

				if (lastIndex < text.length) {
					var tempNode = new CKEDITOR.dom.text(text.substring(lastIndex));

					var markNode = t.span.clone();
					markNode.append(tempNode);
					fragment.append(markNode);
					if (markIndex > lastIndex && markIndex < match.start) {
						t.mark(textNode, markIndex - lastIndex, bookmark);
					}
					markNode.remove(true);

					//标签在最后一段未匹配的文字里面
					if (markIndex > lastIndex) {// && markIndex < text.length
						t.mark(tempNode, markIndex - lastIndex, bookmark);

					}
					markNode.remove(true);
				}



				//这里通过fragment创建的bookmark不能和文档上的bookmark是不同的。
				range.deleteContents();
				range.insertNode(fragment);
				fragment.remove(true);
				//debugger;
				//处理和替换后再替换页面内容。
			});
			//debugger;
			//range && t.onInput(range);
		},
		mark: function (node, index, bm) {
			var t = this;
			var textRange = new CKEDITOR.dom.range(t.document);
			textRange.setStart(node, index);
			textRange.collapse(true);
			var mark = textRange.createBookmark();
			mark.startNode.setAttribute("id", bm.startNode);
		},
		onInput: function (range) {
			//debugger;
			var startNode = range.startContainer;
			var endNode = range.endContainer;
			var startOffset = range.startOffset;
		},
		onMouseDown: function () {
			this.focus();
		},
		destroy: function () {
		},
		focus: function () {
			this.editor.focusManager.focus();
		},
		refresh: function () {


		},
		reset: function () { }
	});
	// Context menu constructing.
	var addButtonCommand = function (editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder) {
		editor.addCommand(commandName, command);

		// If the "menu" plugin is loaded, register the menu item.
		editor.addMenuItem(commandName,
			{
				label: buttonLabel,
				command: commandName,
				group: menugroup,
				order: menuOrder
			});
	};

	CKEDITOR.plugins.add(plugin.name, {
		requires: ['tabletools'],
		beforeInit: function (editor) {
			editor.config.menu_groups = plugin.name + '_suggest,' + editor.config.menu_groups;
		},
		init: function (editor) {
			// 当文本粘帖到编辑器中，删除 span[data-scaytid]
			var dataFilter = editor.dataProcessor && editor.dataProcessor.dataFilter;
			var dataFilterRules = {
				elements: {
					span: function (element) {
						var attrs = element.attributes;
						if (attrs && attrs['mc'])
							delete element.name;
					},
					img: function (element) {
						if (element.attributes['mc']) {
							delete element.name;
						}
					}
				}
			};
			dataFilter && dataFilter.addRules(dataFilterRules);

			var mainSuggestions = {};
			if (editor.contextMenu && editor.addMenuItems) {
				editor.contextMenu.addListener(function (element, selection) {
					if (!element || !element.getAttribute('data-highlight-sug'))
						return null;

					// Remove unused commands and menuitems
					for (m in mainSuggestions) {
						delete editor._.menuItems[m];
						delete editor._.commands[m];
					}
					mainSuggestions = {};

					var items_suggestion = element.getAttribute('data-highlight-sug') || '';
					items_suggestion = items_suggestion.split('|');
					//editor.addMenuGroup('highlight_suggest');
					for (var i = 0, l = items_suggestion.length; i < l; i += 1) {
						var commandName = 'highlight_suggestion_' + items_suggestion[i].replace(' ', '_');
						var exec = (function (el, s) {
							return {
								exec: function () {
									var text = new CKEDITOR.dom.text(s, editor.document);
									text.replace(el);
								}
							};
						})(element, items_suggestion[i]);

						addButtonCommand(editor, 'button_' + commandName, items_suggestion[i],
									commandName, exec, 'highlight_suggest', i + 1);
						mainSuggestions[commandName] = CKEDITOR.TRISTATE_OFF;
					}
					return mainSuggestions;
				});
			}

			editor.on('instanceReady', function () {
				plugin.addInstance(editor);
			});
		},
		afterInit: function (editor) {
			// 防止标记在清除格式时被删除
			var elementsPathFilters,
					filter = function (element) {
						if (element.hasAttribute('mc'))
							return false;
					};

			if (editor._.elementsPath && (elementsPathFilters = editor._.elementsPath.filters))
				elementsPathFilters.push(filter);
			editor.addRemoveFormatFilter && editor.addRemoveFormatFilter(filter);
		}
	});
})();
